{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center>\n",
    "<img src=\"../../img/ods_stickers.jpg\">\n",
    "## Открытый курс по машинному обучению. Сессия № 2\n",
    "</center>\n",
    "Автор материала: программист-исследователь Mail.ru Group, старший преподаватель Факультета Компьютерных Наук ВШЭ Юрий Кашницкий. Материал распространяется на условиях лицензии [Creative Commons CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <center>Тема 7. Обучение без учителя\n",
    "## <center>Часть 1. Метод главных компонент"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Существует несколько эквивалентных математических формулировок метода главных компонент. Основная идея заключается в нахождении таких попарно ортогональных направлений в исходном многомерном пространстве, вдоль которых данные имеют наибольший разброс (выборочную дисперсию). Эти направления называются главными компонентами. \n",
    "\n",
    "Другая формулировка PCA – для данной многомерной случайной величины построить такое ортогональное преобразование координат, что в результате корреляции между отдельными координатами обратятся в ноль. Таким образом, задача сводится к диагонализации матрицы ковариаций, что эквивалентно нахождению сингулярного разложения матрицы исходных данных. Хотя формально задачи сингулярного разложения матрицы данных и спектрального разложения ковариационной матрицы совпадают, алгоритмы вычисления сингулярного разложения напрямую, без вычисления ковариационной матрицы и её спектра, более эффективны и устойчивы.\n",
    "\n",
    "Ещё одной из формулировок задачи PCA является нахождение такой $d$-мерной плоскости в признаковом пространстве, что ошибка проецирования обучающих объектов на нее будет минимальной. Направляющие векторы этой плоскости и будут первыми $d$ главными компонентами."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Интуиция метода"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рассмотрим двухмерный пример, где вместо двух признаков можно завести их линейную комбинацию.\n",
    "\n",
    "Для \"хорошего\"направления сумма расстояний до выбранной прямой (гиперплоскости) минимальна. Слева показано \"хорошее\" направление, справа – \"плохое\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src='../../img/pca_good_bad_direction.png'>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Пусть прямая задается единичным вектором $u$. Минимизация расстояния от точки $x^{(i)}$ до прямой эквивалентна минимизации угла между радиус-вектором $x^{(i)}$ и вектором $u$. Косинус такого угла можно выразить так:\n",
    "\n",
    "$$ \\Large cos~\\theta = \\frac{x^{{(i)}^T} u }{||x^{(i)}||}, ||u|| = 1$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Задача состоит в том, чтобы найти такое направление $u$, что сумма квадратов проекций минимальна (то есть сумма квадратов косинусов максимальна). \n",
    "\n",
    "$$\\Large u = \\arg\\max_{||u||=1} \\frac{1}{m} \\sum_{i=1}^{m} {(x^{{(i)}^T} u )^2} = \\arg\\max_{||u||=1} \\frac{1}{m} \\sum_{i=1}^{m} {(u^Tx^{{(i)}} )(x^{{(i)}^T} u )} =$$ $$ \\Large  \\arg\\max_{||u||=1} u^T \\left[\\frac{1}{m} \\sum_{i=1}^{m}{x^{(i)} x^{(i)^T}} \\right] u $$\n",
    "\n",
    "Удобно ввести ковариационную матрицу $$\\Large \\Sigma = \\sum_{i=1}^{m}{x^{(i)} x^{(i)^T}} = X^TX \\in \\mathbb{R}^{n \\times n}$$\n",
    "\n",
    "Тогда решается оптимизационная задача:\n",
    "\n",
    "$$\\Large \\max_{u}{u^T \\Sigma u},~~~u^Tu = 1$$\n",
    "\n",
    "Лагранжиан для этой задачи: $$\\Large L(u, \\lambda) = u^T \\Sigma u - \\lambda (u^Tu-1)$$\n",
    "\n",
    "Его производная по $u$: $$\\Large \\nabla_u L = \\Sigma u - \\lambda u = 0 \\Leftrightarrow \\Sigma u =\\lambda u$$\n",
    "\n",
    "То есть $u$ должен быть собственным вектором ковариационной матрицы $\\Sigma$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Обобщение: если хотим ввести $k$-размерное пространства вместо $n$- размерного, берем $k$ собственных векторов ковариационной матрицы $\\Sigma$, соответсвующих $k$ максимальным собственным значениям. Тогда новым образом каждой точки $x^{(i)} \\in \\mathbb{R}^n$ обучающей выборки будет \n",
    "$$\\Large z^{(i)} = \\left[\n",
    "\\begin{array}{c}\n",
    "u_1^Tx^{(i)} \\\\ \n",
    "u_2^Tx^{(i)} \\\\ \n",
    "\\ldots \\\\ \n",
    "u_k^Tx^{(i)} \n",
    "\\end{array}\n",
    "\\right] \\in \\mathbb{R}^k$$\n",
    "\n",
    "Один из наиболее эффективных способов нахождения собственных векторов матрицы $\\Sigma$ - использование сингулярного разложения исходной матрицы $X$:\n",
    "\n",
    "$$\\Large X = UDV^T,$$\n",
    "\n",
    "где $U \\in R^{m \\times m}$,  $V \\in R^{n \\times n}$, а $D \\in R^{m \\times n}$ - диагональная матрица вида\n",
    "\n",
    "<img src='../../img/svd_diag_matrix.png' width=70%>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Сингулярное разложение\n",
    "\n",
    "Рассмотрим более подробно задачу о сингулярном разложении матрицы $X \\in \\mathbb{R}^{m \\times n}$. \n",
    "*Сингулярным разложением* матрицы $X$ называется представление её в виде $X = UD V^T$, где:\n",
    "\n",
    " - $D$ есть $m\\times n$ матрица у которой элементы, лежащие на главной диагонали, неотрицательны, а все остальные элементы равны нулю.\n",
    " - $U$ и $V$ – ортогональные матрицы порядка $m$  и $n$ соответственно.\n",
    " \n",
    "Элементы главной диагонали матрицы $D$ называются *сингулярными числами* матрицы $X$, а столбцы $U$ и $V$ левыми и правыми *сингулярными векторами* матрицы $X$.\n",
    "\n",
    "Заметим, что матрицы $XX^T$ и $X^TX$ являются симметрическими неотрицательно определенными матрицами, и поэтому ортогональным преобразованием могут быть приведены к диагональному виду, причем на диагонали будут стоять неотрицательные собственные значения этих матриц.\n",
    "\n",
    "В силу указанных выше свойств матриц $X^TX$ и $XX^T$ сингулярное разложение матрицы $X$ тесно связано с задачей о спектральном разложении этих матриц. Более точно:\n",
    "- Левые сингулярные векторы матрицы $X$ – это собственные векторы матрицы $XX^T$.\n",
    "- Правые сингулярные векторы матрицы $X$ – это собственные векторы матрицы $X^TX$.\n",
    "- Сингулярные числа матрицы $X$ - это корни из собственных значений матрицы $X^TX$ (или $XX^T$).\n",
    "\n",
    "Таким образом, для нахождения сингулярного разложения матрицы $X$ необходимо, найти собственные векторы и значения матриц $X^TX$ и $XX^T$ и составить из них матрицы $U, V, D$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Алгоритм PCA\n",
    "1. Определить $k<n$ – новую размерность\n",
    "2. Вычесть из $X$ среднее, то есть заменить все $\\Large x^{(i)}$  на $$\\Large  x^{(i)} - \\frac{1}{m} \\sum_{i=1}^{m}{x^{(i)}}$$\n",
    "3. Привести данные к единичной дисперсии: посчитать $$\\Large  \\sigma_j^2 = \\frac{1}{m} \\sum_{i=1}^{m}{(x^{(i)})^2}$$\n",
    "и заменить $\\Large x_j^{(i)}$ на $\\Large \\frac{x_j^{(i)}}{\\sigma_j}$ \n",
    "4. Найти сингулярное разложение матрицы $X$:\n",
    "$$\\Large X = UDV^T$$\n",
    "5. Положить $V =$ [$k$ левых столбцов матрицы $V$]\n",
    "6. Вернуть новую матрицу $$\\Large Z = XV \\in \\mathbb{R}^{m \\times k}$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Двухмерный пример\n",
    "\n",
    "Чтобы понять геометрический смысл главных компонент, рассмотрим в качестве примера выборку из двухмерного нормального распределения с явно выраженным \"главным\" направлением. Выделим в ней главные компоненты и посмотрим, какую долю дисперсии объясняет каждая из них."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.decomposition import PCA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(0)\n",
    "mean = np.array([0.0, 0.0])\n",
    "cov = np.array([[1.0, -1.0], \n",
    "                [-2.0, 3.0]])\n",
    "X = np.random.multivariate_normal(mean, cov, 300)\n",
    "\n",
    "pca = PCA()\n",
    "pca.fit(X)\n",
    "print('Proportion of variance explained by each component:\\n' +\\\n",
    "      '1st component - %.2f,\\n2nd component - %.2f\\n' %\n",
    "      tuple(pca.explained_variance_ratio_))\n",
    "print('Directions of principal components:\\n' +\\\n",
    "      '1st component:', pca.components_[0],\n",
    "      '\\n2nd component:', pca.components_[1])\n",
    "\n",
    "plt.figure(figsize=(10,10))\n",
    "plt.scatter(X[:, 0], X[:, 1], s=50, c='r')\n",
    "for l, v in zip(pca.explained_variance_ratio_, pca.components_):\n",
    "    d = 5 * np.sqrt(l) * v\n",
    "    plt.plot([0, d[0]], [0, d[1]], '-k', lw=3)\n",
    "plt.axis('equal')\n",
    "plt.title('2D normal distribution sample and its principal components')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Первая главная компонента (ей соответствует более длинный вектор) объясняет более 90% дисперсии исходных данных. Это говорит о том, что она содержит в себе почти всю информацию о расположении выборки в пространстве, и вторая компонента может быть опущена. Спроецируем данные на первую компоненту."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Keep enough components to explain 90% of variance\n",
    "pca = PCA(0.90)\n",
    "X_reduced = pca.fit_transform(X)\n",
    "\n",
    "# Map the reduced data into the initial feature space\n",
    "X_new = pca.inverse_transform(X_reduced)\n",
    "\n",
    "plt.figure(figsize=(10,10))\n",
    "plt.plot(X[:, 0], X[:, 1], 'or', alpha=0.3)\n",
    "plt.plot(X_new[:, 0], X_new[:, 1], 'or', alpha=0.8)\n",
    "plt.axis('equal')\n",
    "plt.title('Projection of sample onto the 1st principal component')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Мы понизили размерность данных вдвое, при этом сохранив наиболее значимые черты. В этом заключается основной принцип понижения размерности – приблизить многомерный набор данных с помощью данных меньшей размерности, сохранив при этом как можно больше информации об исходных данных."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Визуализация многомерных данных\n",
    "\n",
    "Одним из применений метода главных компонент является визуализации многомерных данных в двухмерном (или трехмерном) пространстве. Для этого необходимо взять первые две главных компоненты и спроецировать данные на них. При этом, если признаки имеют различную природу, их следует отмасштабировать. Основные способы масштабирования:\n",
    "- На единичную дисперсию по осям (масштабы по осям равны средним квадратичным отклонениям — после этого преобразования ковариационная матрица совпадает с матрицей коэффициентов корреляции).\n",
    "- На равную точность измерения (масштаб по оси пропорционален точности измерения данной величины).\n",
    "- На равные требования в задаче (масштаб по оси определяется требуемой точностью прогноза данной величины или допустимым её искажением — уровнем толерантности). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Пример с набором данных Iris"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn import datasets\n",
    "\n",
    "iris = datasets.load_iris()\n",
    "X, y = iris.data, iris.target\n",
    "\n",
    "pca = PCA(n_components=2)\n",
    "X_reduced = pca.fit_transform(X)\n",
    "\n",
    "print(\"Meaning of the 2 components:\")\n",
    "for component in pca.components_:\n",
    "    print(\" + \".join(\"%.3f x %s\" % (value, name)\n",
    "                     for value, name in zip(component,\n",
    "                                            iris.feature_names)))\n",
    "plt.figure(figsize=(10,7))\n",
    "plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y, s=70, cmap='viridis')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Пример с набором данных digits\n",
    "\n",
    "Рассмотрим применение метода главных компонент для визуализации данных из набора изображений рукописных цифр."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.datasets import load_digits\n",
    "\n",
    "digits = load_digits()\n",
    "X = digits.data\n",
    "y = digits.target\n",
    "\n",
    "pca = PCA(n_components=2)\n",
    "X_reduced = pca.fit_transform(X)\n",
    "\n",
    "print('Projecting %d-dimensional data to 2D' % X.shape[1])\n",
    "\n",
    "plt.figure(figsize=(12,10))\n",
    "plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y, \n",
    "            edgecolor='none', alpha=0.7, s=40,\n",
    "            cmap=plt.cm.get_cmap('nipy_spectral', 10))\n",
    "plt.colorbar()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Полученная картинка позволяет увидеть зависимости между различными цифрами. Например, цифры 0 и 6 располагаются в соседних кластерах, что говорит об их схожем написании. Наиболее \"разбросанный\" (по другим кластерам) – это кластер, соответствующий цифре 8, что говорит о том, что она имеет много различных написаний, делающих её схожей со многими другими цифрами.\n",
    "\n",
    "Посмотрим, как выглядят первые две главные компоненты."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f, (ax1, ax2) = plt.subplots(1, 2, sharey=True)\n",
    "\n",
    "im = pca.components_[0]\n",
    "ax1.imshow(im.reshape((8, 8)), cmap='binary')\n",
    "ax1.set_xticks([])\n",
    "ax1.set_yticks([])\n",
    "ax1.set_title('First principal component')\n",
    "\n",
    "im = pca.components_[1]\n",
    "ax2.imshow(im.reshape((8, 8)), cmap='binary')\n",
    "ax2.set_xticks([])\n",
    "ax2.set_yticks([])\n",
    "ax2.set_title('Second principal component')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Сжатие данных\n",
    "\n",
    "Другим применением PCA является снижение размерности данных для их сжатия. Рассмотрим, как влияет число отбираемых главных компонент (на которые осуществляется проекция) на качество восстановления исходного изображения."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(4,2))\n",
    "plt.imshow(X[15].reshape((8, 8)), cmap='binary')\n",
    "plt.xticks([])\n",
    "plt.yticks([])\n",
    "plt.title('Source image')\n",
    "plt.show()\n",
    "\n",
    "fig, axes = plt.subplots(8, 8, figsize=(10, 10))\n",
    "fig.subplots_adjust(hspace=0.1, wspace=0.1)\n",
    "\n",
    "for i, ax in enumerate(axes.flat):\n",
    "    pca = PCA(i + 1).fit(X)\n",
    "    im = pca.inverse_transform(pca.transform(X[15].reshape(1, -1)))\n",
    "\n",
    "    ax.imshow(im.reshape((8, 8)), cmap='binary')\n",
    "    ax.text(0.95, 0.05, 'n = {0}'.format(i + 1), ha='right',\n",
    "            transform=ax.transAxes, color='red')\n",
    "    ax.set_xticks([])\n",
    "    ax.set_yticks([])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как понять, какое число главных компонент достаточно оставить? Для этого может оказаться полезным следующий график, выражающий зависимость общей доли объясняемой дисперсии от числа главных компонент."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pca = PCA().fit(X)\n",
    "\n",
    "plt.figure(figsize=(10,7))\n",
    "plt.plot(np.cumsum(pca.explained_variance_ratio_), color='k', lw=2)\n",
    "plt.xlabel('Number of components')\n",
    "plt.ylabel('Total explained variance')\n",
    "plt.xlim(0, 63)\n",
    "plt.yticks(np.arange(0, 1.1, 0.1))\n",
    "plt.axvline(21, c='b')\n",
    "plt.axhline(0.9, c='r')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pca = PCA(0.9).fit(X)\n",
    "print('We need %d components to explain 90%% of variance' \n",
    "      % pca.n_components_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Предобработка данных"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Метод главных компонент часто используется для предварительной обработки данных перед обучением классификатора. В качестве примера такого применения рассмотрим задачу о распознавании лиц. \n",
    "\n",
    "Для начала посмотрим на исходные данные."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "from sklearn import datasets\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "lfw_people = datasets.fetch_lfw_people(min_faces_per_person=50, \n",
    "                resize=0.4, data_home='../../data/faces')\n",
    "\n",
    "print('%d objects, %d features, %d classes' % (lfw_people.data.shape[0],\n",
    "      lfw_people.data.shape[1], len(lfw_people.target_names)))\n",
    "print('\\nPersons:')\n",
    "for name in lfw_people.target_names:\n",
    "    print(name)\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Распределение целевого класса:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i, name in enumerate(lfw_people.target_names):\n",
    "    print(\"{}: {} photos.\".format(name, (lfw_people.target == i).sum()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(8, 6))\n",
    "\n",
    "for i in range(15):\n",
    "    ax = fig.add_subplot(3, 5, i + 1, xticks=[], yticks=[])\n",
    "    ax.imshow(lfw_people.images[i], cmap='bone')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train, X_test, y_train, y_test = \\\n",
    "    train_test_split(lfw_people.data, lfw_people.target, random_state=0)\n",
    "\n",
    "print('Train size:', X_train.shape[0], 'Test size:', X_test.shape[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вместо обычного PCA воспользуемся его приближенной версией (randomized PCA), которая позволяет существенно ускорить работу алгоритма на больших наборах данных. Выделим 100 главных компонент. Как видно, они объясняют более 90% дисперсии исходных данных."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pca = PCA(n_components=100, svd_solver='randomized')\n",
    "pca.fit(X_train)\n",
    "\n",
    "print('100 principal components explain %.2f%% of variance' %\n",
    "      (100 * np.cumsum(pca.explained_variance_ratio_)[-1]))\n",
    "plt.figure(figsize=(10,7))\n",
    "plt.plot(np.cumsum(pca.explained_variance_ratio_), lw=2, color='k')\n",
    "plt.xlabel('Number of components')\n",
    "plt.ylabel('Total explained variance')\n",
    "plt.xlim(0, 100)\n",
    "plt.yticks(np.arange(0, 1.1, 0.1))\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрим на главные компоненты (или главные \"лица\"). Видим, что первые главные компоненты несут в себе информацию в основном об освещении на фотографии, в то время как оставшиеся выделяют какие-то отдельные черты человеческого лица - глаза, брови и другие."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(16, 6))\n",
    "for i in range(30):\n",
    "    ax = fig.add_subplot(3, 10, i + 1, xticks=[], yticks=[])\n",
    "    ax.imshow(pca.components_[i].reshape((50, 37)), cmap='bone')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PCA позволяет посмотреть на \"среднее\" лицо – тут считается среднее по каждому новому признаку."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.imshow(pca.mean_.reshape((50, 37)), cmap='bone')\n",
    "plt.xticks([])\n",
    "plt.yticks([])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Перейдем теперь непосредственно к классификации. Мы сократили размерность данных (с 1850 признаков до 100), что позволяет существенно ускорить  работу стандартных алгоритмов обучения. Настроим SVM с RBF-ядром и посмотрим на результаты классификации."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "from sklearn.svm import LinearSVC\n",
    "\n",
    "X_train_pca = pca.transform(X_train)\n",
    "X_test_pca = pca.transform(X_test)\n",
    "\n",
    "clf = LinearSVC(random_state=17).fit(X_train_pca, y_train)\n",
    "y_pred = clf.predict(X_test_pca)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.metrics import (accuracy_score, classification_report,\n",
    "                             confusion_matrix)\n",
    "\n",
    "print(\"Accuracy: %f\" % accuracy_score(y_test, y_pred))\n",
    "print(classification_report(y_test, y_pred, target_names=lfw_people.target_names))\n",
    "\n",
    "M = confusion_matrix(y_test, y_pred)\n",
    "M_normalized = M.astype('float') / M.sum(axis=1)[:, np.newaxis]\n",
    "\n",
    "plt.figure(figsize=(10,10))\n",
    "im = plt.imshow(M_normalized, interpolation='nearest', cmap='Greens')\n",
    "plt.colorbar(im, shrink=0.71)\n",
    "tick_marks = np.arange(len(lfw_people.target_names))\n",
    "plt.xticks(tick_marks - 0.5, lfw_people.target_names, rotation=45)\n",
    "plt.yticks(tick_marks, lfw_people.target_names)\n",
    "plt.tight_layout()\n",
    "plt.ylabel('True person')\n",
    "plt.xlabel('Predicted person')\n",
    "plt.title('Normalized confusion matrix')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Полезные ссылки\n",
    "- [Формальное описание PCA](http://www.machinelearning.ru/wiki/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D1%8B%D1%85_%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82)\n",
    "- [PCA in 3 steps](http://sebastianraschka.com/Articles/2015_pca_in_3_steps.html)\n",
    "- [SVD](https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D0%BD%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D0%BE%D0%B5_%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5)\n",
    "- [Eigenface](https://en.wikipedia.org/wiki/Eigenface)\n",
    "- [sklearn.decomposition](http://scikit-learn.org/stable/modules/classes.html#module-sklearn.decomposition)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  },
  "name": "lesson8_part2_PCA.ipynb"
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
